package com.itheima.prize.msg;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itheima.prize.commons.config.RedisKeys;
import com.itheima.prize.commons.db.entity.*;
import com.itheima.prize.commons.db.service.CardGameProductService;
import com.itheima.prize.commons.db.service.CardGameRulesService;
import com.itheima.prize.commons.db.service.CardGameService;
import com.itheima.prize.commons.db.service.GameLoadService;
import com.itheima.prize.commons.utils.RedisUtil;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.*;

/**
 * 活动信息预热，每隔1分钟执行一次
 * 查找未来1分钟内（含），要开始的活动
 */
@Component
public class GameTask {
    private final static Logger log = LoggerFactory.getLogger(GameTask.class);
    @Autowired
    private CardGameService gameService;
    @Autowired
    private CardGameProductService gameProductService;
    @Autowired
    private CardGameRulesService gameRulesService;
    @Autowired
    private GameLoadService gameLoadService;
    @Autowired
    private RedisUtil redisUtil;

    @Scheduled(cron = "0 * * * * ?")
    public void execute() {
        System.out.printf("scheduled!" + new Date());
        //TODO：任务5.1-缓存预热-调度写入缓存
        //构建条件（开始时间大于现在时间 并且 开始时间小于等于现在时间加一分钟）查询活动列表
        //做到每分钟扫描一遍carduser表，找出一分钟内要开始的活动装进list中

        QueryWrapper<CardGame> gameQueryWrapper = new QueryWrapper<CardGame>();
        gameQueryWrapper.gt("starttime", LocalDateTime.now())
                .le("starttime", LocalDateTime.now().plusMinutes(1));
        List<CardGame> games = gameService.list(gameQueryWrapper);
        //分布式锁，防止重复启动任务
       /* if (!redisUtil.setNx("game_task_" + now.getTime(), 1, 60L)) {
            log.info("task started by another server!");
            return;
        }*/

        //判断没有活动 返回
        if (games.size() == 0) {
            log.info("没有一分钟内要开始的活动");
            return;
        }
        //拿到单个game元素  开始处理单个活动
        for (CardGame game : games) {
            System.out.println("开始预热活动"+game.getId()+game.getTitle());
            long gameStartTime = game.getStarttime().getTime();
            long gameEndTime = game.getEndtime().getTime();
            //持续时间不能为负数
            if(gameEndTime<gameStartTime){
                System.out.println("结束时间比开始时间早？你傻逼吗？");
            }
            long duration = gameEndTime - gameStartTime; //活动持续时间 按ms计算

            long expireTime =duration/1000+3600; //缓存过期时间  s

            //设置活动为已加载并更新数据库
            game.setStatus(1);
            gameService.updateById(game);

            //将活动信息缓存进redis里
            redisUtil.set(RedisKeys.INFO + game.getId(), game, -1);

            Integer gameId = game.getId(); //拿到该活动id
            //查询活动相关的奖品列表和数量
            List<CardProductDto> products = gameLoadService.getByGameId(gameId);
            if(products==null){
                System.out.println("发现一个没有奖品的活动 没奖品你抽尼玛呢");
                return;
            }
            List<Long> tokenList = new LinkedList<>();//令牌桶的list


            //遍历奖品列表 得到不同类型的奖品数量和奖品信息
            for (CardProductDto product : products) {
                long tokenNum = product.getAmount(); //奖品数量，也是马上将要生成该奖品种类的令牌数量
                //记得判断奖品数量别为空 管理端要写奖品数量！！！
                    //开始循环生成每种奖品数量的令牌
                    for (long i = 0; i < tokenNum; i++) {
                        long rnd = gameStartTime + new Random().nextInt((int) duration);
                        long token = rnd * 1000 + new Random().nextInt(999);
                        //将令牌添加进令牌桶list中
                        tokenList.add(token);
                        //设置奖品映射信息
                        redisUtil.set(RedisKeys.TOKEN+game.getId()+"_"+token,product,expireTime);

                    }



            }
            //将该活动的令牌们从小到大排序
            Collections.sort(tokenList);
            //将抽奖令牌桶缓存进redis里
            redisUtil.rightPushAll(RedisKeys.TOKENS+game.getId(),tokenList);
            //指定令牌桶的失效时间
            redisUtil.expire(RedisKeys.TOKENS+game.getId(),expireTime);

            //根据活动id查询该活动配置下的用户等级
            QueryWrapper<CardGameRules> wrapper =new QueryWrapper<>();
            wrapper.eq("gameid",game.getId());

            //该活动的策略集合 里面是不同等级用户的抽奖次数和中奖次数 中奖概率等策略
            List<CardGameRules> gameRules = gameRulesService.list(wrapper);
            if(gameRules!=null){
                //往redis里缓存活动策略
                for (CardGameRules gameRule:gameRules){
                    redisUtil.hset(RedisKeys.MAXGOAL+game.getId(),gameRule.getUserlevel()+"",gameRule.getGoalTimes(),expireTime);
                    redisUtil.hset(RedisKeys.MAXENTER+game.getId(),gameRule.getUserlevel()+"",gameRule.getEnterTimes(),expireTime);
                }
            }



        }
        log.info("预热完成，一共预热了： +games.size()+个活动");

    }
}



